<?php
namespace App\Services\User;

use Illuminate\Support\Facades\DB;
use App\Services\Log\Log\LogLogic;
/**
 * Class Auth  权限管理
 * @package App\Services\User
 */
class Auth{
    // 增加一条权限记录
    public function add(array $data): array
    {
        $data['status'] = '1';
        ValidateAuth::checkValidate($data, ['name', 'app_name', 'server', 'module', 'controller', 'action', 'other_check', 'merge_auth']);
        $data['create_time'] = NOW;
        $data['update_time'] = NOW;
        try{
            $res = \App\Models\User\Auth::insertGetId($data);
        }catch (\Exception $e) {
            // 当唯一键重复等报错时
            return server_err(null, '数据库异常:' . $e->getMessage());
        }
        return auto_return($res, '成功增加一条权限记录', '增加一条权限记录失败');
    }
    // 编辑一条权限
    public function edit(array $data): array
    {
        $oldInfo = \App\Models\User\Auth::where('id',$data['id'])->first();
        if (!$oldInfo) {
            return \verify_err(null, '没有找到相关权限信息');
        }
        $oldInfo = $oldInfo->toArray();

        $data = \make_map($data);
        $mergeInfo = array_merge($oldInfo, $data);
        //$mergeInfo['merge_auth'] = $mergeInfo['server'] . '/' . $mergeInfo['module'] . '/' .  $mergeInfo['controller'] . '/' .  $mergeInfo['action'];

        $mergeInfo['new_auth'] = 'App' . '/' . 'Modules' . '/' . ucfirst(strtolower($data['module'])) . '/' .  ucfirst(strtolower($data['controller'])) . '@' .  $data['action'];
        $save = \make_map($mergeInfo, ['name', 'app_name', 'server', 'module', 'controller', 'action', 'merge_auth','new_auth']);
        if ($data['other_check'] !== '{}') {
            $save['other_check'] = $data['other_check'];
        }
        $res = \App\Models\User\Auth::where('id',$data['id'])->update($save);
        // 写入日志
        $logData['id'] = session('userId');
        $logData['name'] = session('username');
        $logData['type'] = 2;
        $logData['record'] = "权限";
        (new LogLogic())->addLog($logData);
        return auto_return($res, '成功编辑一条权限', '编辑一条权限失败');
    }
    // 停用一条权限记录
    public function stop(array $data): array
    {
        $_where['id'] = $data['id'];
        $res = \App\Models\User\Auth::where($_where)->update(['status' => '0']);
        return auto_return($res, '成功停用一条权限记录', '停用一条权限记录失败');
    }
    // 启用一条权限记录
    public function start(array $data): array
    {
        $_where['id'] = $data['id'];
        $res = \App\Models\User\Auth::where($_where)->update(['status' => '1']);

        return auto_return($res, '成功启用一条权限记录', '启用一条权限记录失败');
    }

    /**
     * 获取权限列表分页数据
     * @param array $data
     * @return array
     * @throws \App\Exceptions\RequestFailException
     */
    public function list(array $data): array
    {
        $data = init_array($data, ['page' => 1, 'list_rows' => 10, 'keywords' => '']);
        ValidateAuth::checkValidate($data, ['page', 'list_rows']);

        $query = \App\Models\User\Auth::where('status', '>=', 0);

        if(isset($data['keywords']) && $data['keywords']){
            $query->where('name', 'like', '%' . $data['keywords'] . '%');
        }
        $res = $query->orderBy('id', 'desc')->paginate(10);
        return auto_success($res);
    }
//    // 对数据进行转换
   private function _columeNumber(string $group, string $key, string $val): array
   {
        if($group == 'server,module'){
           $res = \App\Models\User\Auth::select(DB::raw('count(*) as '.$val),$key)
                     ->where('status', '>=', 0)
                     ->groupBy('server','module')
                     ->get()
                     ->toArray();
        }else if($group == 'server,module,controller'){
             $res = \App\Models\User\Auth::select(DB::raw('count(*) as '.$val),$key)
                     ->where('status', '>=', 0)
                     ->groupBy('server','module','controller')
                     ->get()
                     ->toArray();
        }else{
            $res = \App\Models\User\Auth::select(DB::raw('count(*) as '.$val),$key)
                     ->where('status', '>=', 0)
                     ->groupBy($group)
                     ->get()
                     ->toArray();
        }

       $list = [];
       foreach($res as $k => $v){
           $list[$v[$key]] = $v[$val];
       }
       return $list;
   }

//    # 获取所有可用的权限列表
//    # 一个很烂的实现方法,如有更好的方法请告诉我 李全春 <adv30@126.com> 2017年6月2日16:25:00
   public function tree($data): array
   {
       // SELECT `server`,COUNT(*) AS `server_number` FROM `new-think-user`.`tp_auth` GROUP BY `server`;
       $serverNumber = $this->_columeNumber('server', 'server', 'server_number');
       // SELECT `module`,COUNT(*) AS `module_number` FROM `new-think-user`.`tp_auth` GROUP BY `server`,`module`;
       $moduleNumber = $this->_columeNumber('server,module', 'module', 'module_number');
       // SELECT `controller`,COUNT(*) AS `controller_number` FROM `new-think-user`.`tp_auth` GROUP BY `server`,`module`,`controller`;
       $controllerNumber = $this->_columeNumber('server,module,controller', 'controller', 'controller_number');
       $list =\App\Models\User\Auth::where('status','>=',0)->get();
       if(!$list){
           return null_data(null, '没有可用的权限列表');
       }
       $res = [];
       foreach($list as $k=>$v){
           $res[$v['server']][] = $v;
       }
       $list = $res;
       $res = [];
       foreach($list as $k=>$v){
           foreach($v as $kk=>$vv){
               $res[$k][$vv['module']][] = $vv;
           }
       }
       $list = $res;

       $res = [];
       foreach($list as $k=>$v){
           $kData = [
               'name' => $k,
               'number' => $serverNumber[$k],
               'isShow' => '0',
               'jiangzhe' => [],
               'sub' => []
           ];
           foreach($v as $kk=>$vv){
               $kkData = [
                   'name' => $kk,
                   'number' => $moduleNumber[$kk],
                   'isShow' => '0',
                   'jiangzhe' => [],
                   'sub' => []
               ];
               foreach($vv as $kkk=>$vvv){
                   $kkkData = [
                       'name' => $vvv['controller'],
                       'number' => $controllerNumber[$vvv['controller']],
                       'isShow' => '0',
                       'jiangzhe' => [],
                   ];
                   $vvvArr = $vvv->toArray();
                   $kkkData['sub'][] = [
                       'name' => $vvv['action'],
                       'number' => '1',
                       'isShow' => '0',
                       'jiangzhe' => $vvvArr['id'],
                       'sub' => []
                   ];
                   $kkkData['jiangzhe'][] = $vvvArr['id'];
                   $kkData['jiangzhe'][] = $vvvArr['id'];
                   $kData['jiangzhe'][] = $vvvArr['id'];
                   $kkData['sub'][] = $kkkData;
               }
               $kData['sub'][] = $kkData;
           }

           $res[] = $kData;
       }
       return auto_return($res, '成功获取可用权限列表', '获取可用权限列表失败');
   }
    // 给某个角色一个权限(关系表)
    public function addAuthByRole(array $data): array
    {
        $checkRes = $this->_getValidIds($data['auth_id']);

        if (\SUCCESS_STATUS !== $checkRes['status']) {
            return $checkRes;
        }

        $data['status'] = '1';
        try{
            $res = \App\Models\User\AuthRole::insertGetId($data);
        }catch (\Exception $e) {
            // 当唯一键重复等报错时
            return server_err(null, '数据库异常:' . $e->getMessage());
        }
        // 写入日志
        $logData['id'] = session('userId');
        $logData['name'] = session('username');
        $logData['type'] = 1;
        $logData['record'] = "赋予角色权限";
        (new LogLogic())->addLog($logData);
        return auto_return($res, '给某个角色一个权限成功', '给某个角色一个权限失败');
    }
    // 给某个角色N个权限(关系表)
    public function addAuthsByRole(array $data): array
    {
        $checkRes = $this->_getValidIds($data['auth_ids'], false);
        if (\SUCCESS_STATUS !== $checkRes['status']) {
            return $checkRes;
        } else {
            $data['auth_ids'] = $checkRes['res']['output_ids'];
        }
        $save = [];
        $idsArr = explode(',', $data['auth_ids']);
        foreach ($idsArr as $k => $v) {
            $save[] = ['auth_id' => $v, 'role_id' => $data['role_id'], 'status' => '1'];
        }
        $count = count($idsArr);
        try{
            $res = \App\Models\User\AuthRole::insert($save);
        }catch (\Exception $e) {
            // 当唯一键重复等报错时
            return server_err(null, '数据库异常:' . $e->getMessage());
        }

        return auto_return($res, '给某个角色' . $count . '个权限成功', '给某个角色' . $count . '个权限失败');
    }
    // 移除某个角色N个权限(关系表)
    public function removeAuthsByRole(array $data): array
    {
        $checkRes = $this->_getValidIds($data['auth_ids'], false);
        if (\SUCCESS_STATUS !== $checkRes['status']) {
            return $checkRes;
        } else {
            $data['auth_ids'] = $checkRes['res']['output_ids'];
        }
        $_where['role_id'] = $data['role_id'];
        //$_where['auth_id'] = ['in', $data['auth_ids']];
        $res = \App\Models\User\AuthRole::where($_where)->whereIn('auth_id',[$data['auth_ids']])->delete();
        // 写入日志
        $logData['id'] = session('userId');
        $logData['name'] = session('username');
        $logData['type'] = 3;
        $logData['record'] = "角色的权限";
        (new LogLogic())->addLog($logData);
        return auto_return($res, '移除某个角色相关权限成功', '移除某个角色相关权限失败');
    }
   // 禁用某个角色的所有权限
    public function stopByRole(array $data): array
    {
        $_where['role_id'] = $data['role_id'];
        $save['status'] = '0';
        $res = \App\Models\User\AuthRole::where($_where)->update($save);
        return auto_return($res, '禁用某个角色的所有权限成功', '禁用某个角色的所有权限失败');
    }
    // 启用某个角色的所有权限
    public function startByRole(array $data): array
    {
        $_where['role_id'] = $data['role_id'];
        $save['status'] = '1';
        $res =\App\Models\User\AuthRole::where($_where)->update($save);
        return auto_return($res, '启用某个角色的所有权限成功', '启用某个角色的所有权限失败');
    }
    // 获取某个角色的权限列表分页
    public function listByRole(array $data): array
    {
        //$_where['auth_role.role_id'] = $data['have'] ? ['eq', $data['role_id']] : ['neq', $data['role_id']];
        if(isset($data['have'])){
            $_where[] = ['auth_role.role_id','>=',$data['role_id']];
        }else{
            $_where[] = ['auth_role.role_id','<>',$data['role_id']];
        }
        $_where[] = ['auth_role.status','=','1'];
        $res = \App\Models\User\Auth::leftJoin('auth_role', 'auth.id', '=', 'auth_role.auth_id')
            ->select('auth.id','auth.name','auth_role.role_id', 'auth_role.auth_id','auth_role.status as audit_status','auth_role.create_time')
            ->where($_where)
            ->paginate($data['list_rows'], null, null, $data['page'])
            ->toArray();
        return auto_return($res, '获取某个角色的权限列表分页成功', '获取某个角色的权限列表分页失败');
    }
    // 获取某个角色的权限所有权限列表数据
    public function treeByRole(array $data): array
    {
        $_where['role_id'] = $data['role_id'];
        $_where['status'] = '1';
        $res = \App\Models\User\AuthRole::where($_where)
            ->get();
        return auto_return($res, '获取某个角色的权限所有权限列表数据成功', '获取某个角色的权限所有权限列表数据失败');
    }
    // 获取指定ids的权限
    public function listByIds(array $data): array
    {
        //$_where['id'] = ['in', $data['ids']];
        $list = \App\Models\User\Auth::whereIn('id',[$data['ids']])
            ->get();
        return auto_return($list, '成功获取权限', '没有相关权限');
    }
    // 从redis获取一个角色集的权限,如没有则从mysql数据库中更新一次至redis
    public function getAuthsByRoles(array $data): array
    {
        $redis = app('redis');
        $key = AUTHS_FRIFIX . $data['roles'];
        // 从redis中判断是否具有某种角色集
        $exists = $redis->exists($key);
        // 如果值存在
        if($exists){
            $authList = $redis->smembers($key);
        } else {
            $authResInfo = $this->_rolesFromMysql($data);
            if(SUCCESS_STATUS !== $authResInfo['status']){
                return $authResInfo;
            }

            $data['authList'] = $authList = $authResInfo['res'];
            //将角色集对应的权限数据设置到redis中
            $this->_setRolesToRedis($data, $redis);
        }
        return auto_return($authList, '成功获取该角色集对应的权限列表', '获取该角色集对应的权限列表失败');
    }
    // 从mysql数据库读[角色集的权限]
    private function _rolesFromMysql(array $data): array
    {
        $idsInfo = (new Role)->getIdsByRoles($data);
        if (SUCCESS_STATUS !== $idsInfo['status']){
            return $idsInfo;
        }
        $ids = explode(',', $idsInfo['res']['ids']);
        $list = \App\Models\User\Auth::leftJoin('auth_role', 'auth.id', '=', 'auth_role.auth_id')
            ->select('auth.*', 'auth_role.auth_id', 'auth_role.role_id')
            ->whereIn('auth_role.role_id', $ids)
            ->where('auth_role.status', 1)
            ->where('auth.status', 1)
            ->get()->toArray();
        $authList = [];
        if (count($list) > 0) {
//            $authList = array_column($list, 'merge_auth');
            $authList = array_column($list, 'new_auth');
        }
        return auto_return($authList, '成功获取该角色权限集', '该角色没有权限集');
    }
    // 将角色集对应的权限数据设置到redis中
    private function _setRolesToRedis(array $data, $redis = null): bool
    {
        $redis ?? $redis = app('redis');
        $key = AUTHS_FRIFIX . $data['roles'];
        foreach ($data['authList'] as $auth) {
            $redis->sadd($key, $auth);
        }
        $redis->expire($key, AUTH_EXPIRE);
        return true;
    }
    /**
     * 传入一堆IDS返回权限表中所可使用的IDS
     * $equality 是返回成功的状态码是否是传入的ids数量和传出的数量相等,还是只要有数据返回就成功
     */
    private function _getValidIds(string $ids, bool $equality = true): array
    {
        $inputCount = count(explode(',', $ids));
        $_where['status'] = '1';
        $list = \App\Models\User\Auth::select('id')
            ->where($_where)
            ->whereIn('id',[$ids])
            ->get();
        $outputCount = count($list);

        $list = $list
            ? $outputCount === 1
                ? $list[0]['id']
                : implode(',', $list)
            : '';
        if ($equality) {
            $status = ($inputCount === $outputCount);
        } else {
            $status = !!$list;
        }
        if ($status) {
            return auto_success(['input_ids' => $ids, 'output_ids' => $list], '成功验证可用权限IDS');
        } else {
            return auto_failure(['input_ids' => $ids, 'output_ids' => $list], '验证可用权限IDS失败');
        }
    }

   public function roleByAuth(array $data): array
   {
       $roleList = \App\Models\User\Role::get()->toArray();
       $roleByAuthList = \App\Models\User\Role::join('auth_role', 'role.id', '=', 'auth_role.role_id')
        ->select('auth_role.role_id', 'role.*')
        ->where('auth_role.auth_id', $data['auth_id'])
        ->where('auth_role.status', 1)
        ->get()
        ->toArray();
       $roleListId = [];
       $roleByAuthListId = [];

       foreach ($roleList as $k => $v) {
           $roleListId[] = $v['id'];
       }
       foreach ($roleByAuthList as $k => $v) {
           $roleByAuthListId[] = $v['role_id'];
       }
       $noByAuthListId = array_diff($roleListId, $roleByAuthListId);
       $noByAuthList = []; //未拥有该权限的角色
       foreach ($roleList as $k => $v) {
           if (in_array($v['id'], $noByAuthListId)) {
               $noByAuthList[] = $v;
           }
       }
       for ($i=0; $i<count($roleByAuthList); $i++) {
           $roleByAuthList[$i]["authStatus"] = 1;
           $roleByAuthList[$i]["auth_text"] = "已绑定";
       }
       for ($i=0; $i<count($noByAuthList); $i++) {
           $noByAuthList[$i]["authStatus"] = 0;
           $noByAuthList[$i]["auth_text"] = "未绑定";
       }
       $roleByAuthStatusList = array_merge($roleByAuthList,$noByAuthList);
       //dd($roleByAuthStatusList);
       return auto_success($roleByAuthStatusList,'角色列表获取成功');
   }

    /**
     * 根据id获取该条权限记录
     * @param $data
     * @return array
     * @throws \App\Exceptions\RequestFailException
     */
    public function authInfo($data): array
    {
        $data = init_array($data, ['id' => 0]);
        ValidateAuth::checkValidate($data, ['id']);

        $info = \App\Models\User\Auth::where('id', $data['id'])->first();
        return auto_return($info);
    }
}
